{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h1> Logistic Regression using Spark ML </h1>\n",
    "\n",
    "Set up bucket"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "BUCKET='ai-analytics-solutions-dsongcp'  # CHANGE ME\n",
    "\n",
    "import os\n",
    "os.environ['BUCKET'] = BUCKET"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Setting default log level to \"WARN\".\n",
      "To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<pyspark.sql.session.SparkSession object at 0x7f4ecf4e2850>\n",
      "<SparkContext master=local appName=logistic>\n"
     ]
    }
   ],
   "source": [
    "# Create spark session\n",
    "\n",
    "from pyspark.sql import SparkSession\n",
    "from pyspark import SparkContext\n",
    "\n",
    "sc = SparkContext('local', 'logistic')\n",
    "spark = SparkSession \\\n",
    "    .builder \\\n",
    "    .appName(\"Logistic regression w/ Spark ML\") \\\n",
    "    .getOrCreate()\n",
    "\n",
    "print(spark)\n",
    "print(sc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyspark.mllib.classification import LogisticRegressionWithLBFGS\n",
    "from pyspark.mllib.regression import LabeledPoint"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Read dataset </h2>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "traindays = spark.read \\\n",
    "    .option(\"header\", \"true\") \\\n",
    "    .csv('gs://{}/flights/trainday.csv'.format(BUCKET))\n",
    "traindays.createOrReplaceTempView('traindays')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+----------+------------+\n",
      "|   FL_DATE|is_train_day|\n",
      "+----------+------------+\n",
      "|2015-01-01|        True|\n",
      "|2015-01-02|       False|\n",
      "|2015-01-03|       False|\n",
      "|2015-01-04|        True|\n",
      "|2015-01-05|        True|\n",
      "+----------+------------+\n",
      "\n"
     ]
    }
   ],
   "source": [
    "spark.sql(\"SELECT * from traindays LIMIT 5\").show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "inputs = 'gs://{}/flights/tzcorr/all_flights-00000-*'.format(BUCKET) # 1/30th\n",
    "#inputs = 'gs://{}/flights/tzcorr/all_flights-*'.format(BUCKET)  # FULL\n",
    "flights = spark.read.json(inputs)\n",
    "\n",
    "# this view can now be queried ...\n",
    "flights.createOrReplaceTempView('flights')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Clean up </h2>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainquery = \"\"\"\n",
    "SELECT\n",
    "  f.*\n",
    "FROM flights f\n",
    "JOIN traindays t\n",
    "ON f.FL_DATE == t.FL_DATE\n",
    "WHERE\n",
    "  t.is_train_day == 'True'\n",
    "\"\"\"\n",
    "traindata = spark.sql(trainquery)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Row(ARR_AIRPORT_LAT=33.43416667, ARR_AIRPORT_LON=-112.01166667, ARR_AIRPORT_TZOFFSET=-25200.0, ARR_DELAY=-16.0, ARR_TIME='2015-07-28T18:20:00', CANCELLED=False, CRS_ARR_TIME='2015-07-28T18:36:00', CRS_DEP_TIME='2015-07-28T17:05:00', DEP_AIRPORT_LAT=33.9425, DEP_AIRPORT_LON=-118.40805556, DEP_AIRPORT_TZOFFSET=-25200.0, DEP_DELAY=-3.0, DEP_TIME='2015-07-28T17:02:00', DEST='PHX', DEST_AIRPORT_SEQ_ID='1410702', DISTANCE='370.00', DIVERTED=False, FL_DATE='2015-07-28', ORIGIN='LAX', ORIGIN_AIRPORT_SEQ_ID='1289203', TAXI_IN=6.0, TAXI_OUT=14.0, UNIQUE_CARRIER='AA', WHEELS_OFF='2015-07-28T17:16:00', WHEELS_ON='2015-07-28T18:14:00'), Row(ARR_AIRPORT_LAT=33.43416667, ARR_AIRPORT_LON=-112.01166667, ARR_AIRPORT_TZOFFSET=-25200.0, ARR_DELAY=12.0, ARR_TIME='2015-07-28T21:25:00', CANCELLED=False, CRS_ARR_TIME='2015-07-28T21:13:00', CRS_DEP_TIME='2015-07-28T19:40:00', DEP_AIRPORT_LAT=33.9425, DEP_AIRPORT_LON=-118.40805556, DEP_AIRPORT_TZOFFSET=-25200.0, DEP_DELAY=24.0, DEP_TIME='2015-07-28T20:04:00', DEST='PHX', DEST_AIRPORT_SEQ_ID='1410702', DISTANCE='370.00', DIVERTED=False, FL_DATE='2015-07-28', ORIGIN='LAX', ORIGIN_AIRPORT_SEQ_ID='1289203', TAXI_IN=8.0, TAXI_OUT=12.0, UNIQUE_CARRIER='AA', WHEELS_OFF='2015-07-28T20:16:00', WHEELS_ON='2015-07-28T21:17:00')]\n"
     ]
    }
   ],
   "source": [
    "print(traindata.head(2))  # if this is empty, try changing the shard you are using."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainquery = \"\"\"\n",
    "SELECT\n",
    "  DEP_DELAY, TAXI_OUT, ARR_DELAY, DISTANCE\n",
    "FROM flights f\n",
    "JOIN traindays t\n",
    "ON f.FL_DATE == t.FL_DATE\n",
    "WHERE\n",
    "  t.is_train_day == 'True'\n",
    "\"\"\"\n",
    "traindata = spark.sql(trainquery)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 71:=============================>                            (1 + 1) / 2]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+-----------------+-----------------+-----------------+\n",
      "|summary|         DEP_DELAY|         TAXI_OUT|        ARR_DELAY|         DISTANCE|\n",
      "+-------+------------------+-----------------+-----------------+-----------------+\n",
      "|  count|            211855|           211667|           211144|           216573|\n",
      "|   mean|10.054159684690001|15.85331676643029|5.404321221536013|835.8613631431434|\n",
      "| stddev| 40.26571628278374|9.020269569048152|43.24505614071096|600.3501910195829|\n",
      "|    min|             -36.0|              1.0|            -87.0|          1005.00|\n",
      "|    max|            1461.0|            174.0|           1460.0|           998.00|\n",
      "+-------+------------------+-----------------+-----------------+-----------------+\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "traindata.describe().show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that the counts for the various columns are all different; We have to remove NULLs in the delay variables (these correspond to canceled or diverted flights)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Logistic regression </h2>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 10:>                                                         (0 + 1) / 1]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|summary|         DEP_DELAY|          TAXI_OUT|         ARR_DELAY|         DISTANCE|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|  count|             46355|             46355|             46355|            46355|\n",
      "|   mean| 8.539531873584295|15.421507927947363|3.2853413871211306| 917.660230827311|\n",
      "| stddev|30.700034730525516|  8.41130660980497| 32.98848343691196|592.0960248192869|\n",
      "|    min|             -22.0|               2.0|             -77.0|          1009.00|\n",
      "|    max|             711.0|             178.0|             719.0|           980.00|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "trainquery = \"\"\"\n",
    "SELECT\n",
    "  DEP_DELAY, TAXI_OUT, ARR_DELAY, DISTANCE\n",
    "FROM flights f\n",
    "JOIN traindays t\n",
    "ON f.FL_DATE == t.FL_DATE\n",
    "WHERE\n",
    "  t.is_train_day == 'True' AND\n",
    "  f.dep_delay IS NOT NULL AND \n",
    "  f.arr_delay IS NOT NULL\n",
    "\"\"\"\n",
    "traindata = spark.sql(trainquery)\n",
    "traindata.describe().show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 18:>                                                         (0 + 1) / 1]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|summary|         DEP_DELAY|          TAXI_OUT|         ARR_DELAY|         DISTANCE|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|  count|             46355|             46355|             46355|            46355|\n",
      "|   mean| 8.539531873584295|15.421507927947363|3.2853413871211306| 917.660230827311|\n",
      "| stddev|30.700034730525516|  8.41130660980497| 32.98848343691196|592.0960248192869|\n",
      "|    min|             -22.0|               2.0|             -77.0|          1009.00|\n",
      "|    max|             711.0|             178.0|             719.0|           980.00|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "trainquery = \"\"\"\n",
    "SELECT\n",
    "  DEP_DELAY, TAXI_OUT, ARR_DELAY, DISTANCE\n",
    "FROM flights f\n",
    "JOIN traindays t\n",
    "ON f.FL_DATE == t.FL_DATE\n",
    "WHERE\n",
    "  t.is_train_day == 'True' AND\n",
    "  f.CANCELLED == 'False' AND \n",
    "  f.DIVERTED == 'False'\n",
    "\"\"\"\n",
    "traindata = spark.sql(trainquery)\n",
    "traindata.describe().show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def to_example(fields):\n",
    "    return LabeledPoint(\\\n",
    "              float(fields['ARR_DELAY'] < 15), #ontime? \\\n",
    "              [ \\\n",
    "                  fields['DEP_DELAY'], \\\n",
    "                  fields['TAXI_OUT'],  \\\n",
    "                  fields['DISTANCE'],  \\\n",
    "              ])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "examples = traindata.rdd.map(to_example)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "21/10/12 04:10:06 WARN com.github.fommil.netlib.BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS\n",
      "21/10/12 04:10:07 WARN com.github.fommil.netlib.BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-0.17926510230641074,-0.1353410840270897,0.00047781052266304745] 5.403405250989946\n"
     ]
    }
   ],
   "source": [
    "lrmodel = LogisticRegressionWithLBFGS.train(examples, intercept=True)\n",
    "print(lrmodel.weights,lrmodel.intercept)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "print(lrmodel.predict([6.0,12.0,594.0]))\n",
    "print(lrmodel.predict([36.0,12.0,594.0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9520080900763146\n",
      "0.08390675828170738\n"
     ]
    }
   ],
   "source": [
    "lrmodel.clearThreshold()\n",
    "print(lrmodel.predict([6.0,12.0,594.0]))\n",
    "print(lrmodel.predict([36.0,12.0,594.0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "lrmodel.setThreshold(0.7) # cancel if prob-of-ontime < 0.7\n",
    "print(lrmodel.predict([6.0,12.0,594.0]))\n",
    "print(lrmodel.predict([36.0,12.0,594.0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Predict with the model </h2>\n",
    "\n",
    "First save the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CommandException: 1 files/objects could not be removed.\n"
     ]
    }
   ],
   "source": [
    "!gsutil -m rm -r gs://$BUCKET/flights/sparkmloutput/model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "MODEL_FILE='gs://' + BUCKET + '/flights/sparkmloutput/model'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "gs://ai-analytics-solutions-dsongcp/flights/sparkmloutput/model saved\n"
     ]
    }
   ],
   "source": [
    "lrmodel.save(sc, MODEL_FILE)\n",
    "print('{} saved'.format(MODEL_FILE))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "lrmodel = 0\n",
    "print(lrmodel)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now retrieve the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyspark.mllib.classification import LogisticRegressionModel\n",
    "lrmodel = LogisticRegressionModel.load(sc, MODEL_FILE)\n",
    "lrmodel.setThreshold(0.7)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n"
     ]
    }
   ],
   "source": [
    "print(lrmodel.predict([36.0,12.0,594.0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "print(lrmodel.predict([8.0,4.0,594.0]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Examine the model behavior </h2>\n",
    "\n",
    "For dep_delay=20 and taxiout=10, how does the distance affect prediction?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.6689849289476673\n"
     ]
    }
   ],
   "source": [
    "lrmodel.clearThreshold() # to make the model produce probabilities\n",
    "print(lrmodel.predict([20, 10, 500]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "dist = np.arange(10, 2000, 10)\n",
    "prob = [lrmodel.predict([20, 10, d]) for d in dist]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'probability of ontime arrival')"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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AEJDQ9m6davJsu3btw93d3fc3NxqzGvIXN27d6dFixaV1lmXfZSdnU1BQQEPP/wwFhYWhIaG/uXPYVW5+vbti7X1rRGku3btSlZWVo3LMESu6rJVx9T77DZFUfjhhx8qHI1UxRC5qvueMNbnTArHn6hUKlxdXXWvXVxcavziNqSLFy+SlpaGn58fACtWrGDYsGG88cYbukNQU+QdP348YWFhrFq1CoCcnBycnZ2BWx/o69evmyxbcnJyhf/I5rLP6rqP/jzd1dXV4BnXrFlD//79da8vXrxIaGgof//73zl48GCVeQ2dqy6/P2NnO3jwIE5OTjz44IO6aabYZ3/8njDW50wKx58oVZzfs7CwMHqOwsJCYmJiePPNN2natCmRkZFs2bKF9evX4+zszOzZswHj5125ciXr1q1j0aJFrFixggMHDlQ7r7GzlZaWsm3bNp544gkAs9lnNakui7EzLliwACsrK4YPHw7c+tLZvn07SUlJTJs2jVdffZWCggKj5qrr78/Y++y7776r8EeKKfbZn78nqnO395kUjj9xdXWtcLiuUql0FdxYysrKiImJYdiwYQwaNAiAli1bYmVlhaWlJRERERw9etQkeV1cXABwcnIiKCiI1NRUnJycyM7OBm4dljs6Opok286dO/H19aVly5aA+ewzoM776M/Ts7KyDJZx3bp1/PTTT8ydO1f3pWFjY4ODgwMAnTp14oEHHuDcuXNGzVXX358xs5WXl7NlyxaGDBmim2bsfVbV94SxPmdSOP6kc+fOZGRkkJmZSWlpKcnJyQQGBhpt/Yqi8NZbb+Hp6cm4ceN0029/GAC2bt2Kj48PcOsup+TkZEpLS8nMzCQjI4MuXboYJFtRUREFBQW6n/fs2YOPjw+BgYEkJSUBkJSUxIABA4yeDW6dpgoODta9Nod9dltd95GzszNNmjTh8OHDKIpSoc3dtHPnThYtWsSCBQto3Lixbvr169fRaDQAulzu7u5GywV1//0ZM9vevXvx9PSscJrHmPusuu8JY33OpHfcKuzYsYMPPvgAjUZDeHg4L774otHWffDgQZ5++mnatWuHpeWtuj5lyhS+++47Tp48CYCbmxuxsbG6vwwWLFjAmjVrsLKy4s033yQgIMAg2TIzM3nppZcA0Gg0DB06lBdffJEbN24wadIkrly5QqtWrYiPj8fe3t6o2YqL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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sns.set_style(\"whitegrid\")\n",
    "ax = plt.plot(dist, prob)\n",
    "plt.xlabel('distance (miles)')\n",
    "plt.ylabel('probability of ontime arrival')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'probability of ontime arrival')"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "delay = np.arange(-20, 60, 1)\n",
    "prob = [lrmodel.predict([d, 10, 500]) for d in delay]\n",
    "ax = plt.plot(delay, prob)\n",
    "plt.xlabel('departure delay (minutes)')\n",
    "plt.ylabel('probability of ontime arrival')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2> Evaluate model </h2>\n",
    "\n",
    "Evaluate on the test data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 52:=============================>                            (1 + 1) / 2]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "SELECT\n",
      "  DEP_DELAY, TAXI_OUT, ARR_DELAY, DISTANCE\n",
      "FROM flights f\n",
      "JOIN traindays t\n",
      "ON f.FL_DATE == t.FL_DATE\n",
      "WHERE\n",
      "  t.is_train_day == 'False' AND\n",
      "  f.CANCELLED == 'False' AND \n",
      "  f.DIVERTED == 'False'\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "inputs = 'gs://{}/flights/tzcorr/all_flights-00001-*'.format(BUCKET) # 1/30th\n",
    "flights = spark.read.json(inputs)\n",
    "flights.createOrReplaceTempView('flights')\n",
    "\n",
    "testquery = trainquery.replace(\"t.is_train_day == 'True'\",\"t.is_train_day == 'False'\")\n",
    "print(testquery)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "testdata = spark.sql(testquery)\n",
    "examples = testdata.rdd.map(to_example)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 55:=============================>                            (1 + 1) / 2]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|summary|         DEP_DELAY|          TAXI_OUT|         ARR_DELAY|         DISTANCE|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "|  count|             82184|             82184|             82184|            82184|\n",
      "|   mean| 8.674377007690062|15.676676725396671|3.8409179402316753|838.9512557188747|\n",
      "| stddev|38.764341740364586| 8.505730543334973| 41.25995960185183|600.3088554927516|\n",
      "|    min|             -35.0|               1.0|             -70.0|          1005.00|\n",
      "|    max|            1576.0|             154.0|            1557.0|           998.00|\n",
      "+-------+------------------+------------------+------------------+-----------------+\n",
      "\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "testdata.describe().show()  # if this is empty, change the shard you are using"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "def eval(labelpred):\n",
    "    ''' \n",
    "        data = (label, pred)\n",
    "            data[0] = label\n",
    "            data[1] = pred\n",
    "    '''\n",
    "    cancel = labelpred.filter(lambda data: data[1] < 0.7)\n",
    "    nocancel = labelpred.filter(lambda data: data[1] >= 0.7)\n",
    "    corr_cancel = cancel.filter(lambda data: data[0] == int(data[1] >= 0.7)).count()\n",
    "    corr_nocancel = nocancel.filter(lambda data: data[0] == int(data[1] >= 0.7)).count()\n",
    "    \n",
    "    cancel_denom = cancel.count()\n",
    "    nocancel_denom = nocancel.count()\n",
    "    if cancel_denom == 0:\n",
    "        cancel_denom = 1\n",
    "    if nocancel_denom == 0:\n",
    "        nocancel_denom = 1\n",
    "    return {'total_cancel': cancel.count(), \\\n",
    "            'correct_cancel': float(corr_cancel)/cancel_denom, \\\n",
    "            'total_noncancel': nocancel.count(), \\\n",
    "            'correct_noncancel': float(corr_nocancel)/nocancel_denom \\\n",
    "           }"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "All flights:\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'total_cancel': 14689, 'correct_cancel': 0.8239498944788617, 'total_noncancel': 67495, 'correct_noncancel': 0.9556411586043411}\n",
      "Flights near decision threshold:\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Stage 69:=============================>                            (1 + 1) / 2]\r"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'total_cancel': 714, 'correct_cancel': 0.3711484593837535, 'total_noncancel': 850, 'correct_noncancel': 0.6788235294117647}\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "                                                                                \r"
     ]
    }
   ],
   "source": [
    "# Evaluate model\n",
    "lrmodel.clearThreshold() # so it returns probabilities\n",
    "labelpred = examples.map(lambda p: (p.label, lrmodel.predict(p.features)))\n",
    "print('All flights:')\n",
    "print(eval(labelpred))\n",
    "\n",
    "# keep only those examples near the decision threshold\n",
    "print('Flights near decision threshold:')\n",
    "labelpred = labelpred.filter(lambda data: data[1] > 0.65 and data[1] < 0.75)\n",
    "print(eval(labelpred))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright 2019-2021 Google Inc. Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
